<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Text Clock</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="renderer" content="webkit" />

    <style type="text/css">
        html, body {
            margin: 0;
            padding: 0;
            background: black;
        }

        .root {
            display: flex;
            justify-content: center;
        }

        .container {
            display: block;
            background: black;
        }
    </style>
</head>
<body>
<div class="root">
    <canvas id="container" class="container"></canvas>
</div>

<script type="text/javascript">
    const HOURS = [
        "一点", "二点", "三点", "四点", "五点", "六点", "七点", "八点", "九点", "十点", "十一点", "十二点"
    ];
    const MINUTES = [
        "一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分",
        "十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分", "二十分",
        "二十一分", "二十二分", "二十三分", "二十四分", "二十五分", "二十六分", "二十七分", "二十八分", "二十九分", "三十分",
        "三十一分", "三十二分", "三十三分", "三十四分", "三十五分", "三十六分", "三十七分", "三十八分", "三十九分", "四十分",
        "四十一分", "四十二分", "四十三分", "四十四分", "四十五分", "四十六分", "四十七分", "四十八分", "四十九分", "五十分",
        "五十一分", "五十二分", "五十三分", "五十四分", "五十五分", "五十六分", "五十七分", "五十八分", "五十九分", ""
    ];
    const SECONDS = [
        "一秒", "二秒", "三秒", "四秒", "五秒", "六秒", "七秒", "八秒", "九秒", "十秒",
        "十一秒", "十二秒", "十三秒", "十四秒", "十五秒", "十六秒", "十七秒", "十八秒", "十九秒", "二十秒",
        "二十一秒", "二十二秒", "二十三秒", "二十四秒", "二十五秒", "二十六秒", "二十七秒", "二十八秒", "二十九秒", "三十秒",
        "三十一秒", "三十二秒", "三十三秒", "三十四秒", "三十五秒", "三十六秒", "三十七秒", "三十八秒", "三十九秒", "四十秒",
        "四十一秒", "四十二秒", "四十三秒", "四十四秒", "四十五秒", "四十六秒", "四十七秒", "四十八秒", "四十九秒", "五十秒",
        "五十一秒", "五十二秒", "五十三秒", "五十四秒", "五十五秒", "五十六秒", "五十七秒", "五十八秒", "五十九秒", ""
    ];

    const INTERVAL_UPDATE = 1000;//时间刷新间隔
    const mColorCenetr = "#ffffff";//中心字体颜色
    const mColorCur = "#ff0000";//选中字体颜色
    const mColorCom = "#bdbdbd";//基础色
    const mColorBg = "#000000";//背景颜色
    const mFont = "华文行楷";//字体

    var mWidth, mHeight;//宽高
    var mH, mM, mS;//当前的时分秒
    var mH24;//24制小时
    var mWeek, mDate, mTime;//星期，日期，时间
    var mRadiusH, mRadiusM, mRadiusS;//三个同心圆的半径
    var mDegreeH, mDegreeM, mDegreeS;//时分秒旋转角度
    var mTextSizeCom;//通用字体大小
    var mTextSizeCenter;//中间字体大小
    var mOffset;//中间文字行间隔
    var mCenterHeight;//文字纵向居中高度

    //////////////////////////////////////////////////////////////////
    var canvas;
    var cxt;
    var hd, md, sd, av;

    window.onload = function () {
        initial();
    }

    //适应屏幕大小变化
    window.onresize = function () {
        calcSize();
    }

    function initial() {
        canvas = document.getElementById("container");
        cxt = canvas.getContext("2d");
        calcSize();
        curTime();
        startTask();
    }

    function calcSize() {
        mWidth = window.innerWidth;
        mHeight = window.innerHeight;
        //确保为正方形
        mWidth = mHeight = Math.min(mWidth, mHeight);
        canvas.width = mWidth;
        canvas.height = mHeight;
        // console.log(mWidth + " - " + mHeight);

        //字体大小
        mTextSizeCom = mWidth / 50;//通用字体大小
        mTextSizeCenter = mWidth / 35;//中间字体大小
        mOffset = mTextSizeCenter / 2;//中间文字间隔
        mCenterHeight = (mHeight - mTextSizeCom) / 2;

        //各圆半径
        mRadiusH = mWidth / 6;
        mRadiusM = mRadiusH + 4 * mTextSizeCom;//3+1
        mRadiusS = mRadiusM + 5 * mTextSizeCom;//4+1
        // console.log(mRadiusH + " - " + mRadiusM + " - " + mRadiusS);
    }

    function startTask() {
        setInterval(function () {
            curTime();
            //内嵌一个有限循环
            var times = 0;
            var interval = setInterval(function () {
                if (times > 6) {
                    clearInterval(interval);
                } else {
                    doAnimation();
                    times++;
                }
            }, 10)
        }, INTERVAL_UPDATE);
    }

    //自定义线性循环动画
    function doAnimation() {
        //+ -> 顺时针； - -> 逆时针
        av = av - 0.9;//总偏移量/次数

        if (mM === 0 && mS === 0) {
            mDegreeH = hd + av * 5;//时圈旋转角度是分秒的5倍，线性的旋转30°
        }

        if (mS === 0) {
            mDegreeM = md + av;//线性的旋转6°
        }

        mDegreeS = sd + av;//线性的旋转6°
        drawCanvas();
    }

    //刷新背景
    function updateBg() {
        cxt.fillStyle = mColorBg;
        cxt.fillRect(0, 0, mWidth, mHeight);
    }

    //绘制中间时间/日期/星期
    function drawCenterInfo() {
        cxt.save();
        cxt.fillStyle = mColorCenetr;
        cxt.font = mTextSizeCenter + "px " + mFont;

        //draw time
        var len = (mTime.length -2) * mTextSizeCenter / 2;//数字大小只有文字的一半 //去掉两个符号
        cxt.fillText(mTime, (mWidth - len) / 2, mHeight / 2 - mTextSizeCenter * 3 / 2 - mOffset);

        //draw date
        var len2 = (mDate.length-2) * mTextSizeCenter / 2;//去掉两个符号
        cxt.fillText(mDate, (mWidth - len2) / 2, mHeight / 2 - mTextSizeCenter / 2);

        //draw week
        var len3 = mWeek.length * mTextSizeCenter;
        cxt.fillText(mWeek, (mWidth - len3) / 2, mHeight / 2 + mTextSizeCenter / 2 + mOffset);
        cxt.restore();
    }

    function drawHour() {
        cxt.save();
        //只能旋转角度，以原点(0,0)
        cxt.translate(mWidth / 2, mHeight / 2);
        cxt.rotate(mDegreeH * Math.PI / 180);//* Math.PI / 180
        cxt.translate(-mWidth / 2, -mHeight / 2);//旋转完之后需要移回去

        //时针圈: 360/12 = 30度
        for (var i = 0; i < HOURS.length; i++) {
            cxt.save();
            cxt.translate(mWidth / 2, mHeight / 2);
            cxt.rotate(30 * i * Math.PI / 180);
            cxt.translate(-mWidth / 2, -mHeight / 2);

            if (i + 1 === mH || (i === 11 && mH === 0)) {
                //当前时
                cxt.fillStyle = mColorCur;
            } else {
                cxt.fillStyle = mColorCom;
            }
            cxt.font = mTextSizeCom + "px " + mFont;
            cxt.fillText(HOURS[i], mWidth / 2 + mRadiusH, mCenterHeight);
            cxt.restore();
        }
        cxt.restore();
    }

    function drawMinute() {
        cxt.save();
        //只能旋转角度，以原点(0,0)
        cxt.translate(mWidth / 2, mHeight / 2);
        cxt.rotate(mDegreeM * Math.PI / 180);//* Math.PI / 180
        cxt.translate(-mWidth / 2, -mHeight / 2);//旋转完之后需要移回去

        //分针圈: 360/60 = 6度
        for (var i = 0; i < MINUTES.length; i++) {
            cxt.save();
            cxt.translate(mWidth / 2, mHeight / 2);
            cxt.rotate(6 * i * Math.PI / 180);
            cxt.translate(-mWidth / 2, -mHeight / 2);

            if (i + 1 === mM) {
                //当前时
                cxt.fillStyle = mColorCur;
            } else {
                cxt.fillStyle = mColorCom;
            }
            cxt.font = mTextSizeCom + "px " + mFont;
            cxt.fillText(MINUTES[i], mWidth / 2 + mRadiusM, mCenterHeight);
            cxt.restore();
        }
        cxt.restore();
    }

    function drawSecond() {
        cxt.save();
        //只能旋转角度，以原点(0,0)
        cxt.translate(mWidth / 2, mHeight / 2);
        cxt.rotate(mDegreeS * Math.PI / 180);//* Math.PI / 180
        cxt.translate(-mWidth / 2, -mHeight / 2);//旋转完之后需要移回去

        //秒针圈: 360/60 = 6度
        for (var i = 0; i < MINUTES.length; i++) {
            cxt.save();
            cxt.translate(mWidth / 2, mHeight / 2);
            cxt.rotate(6 * i * Math.PI / 180);
            cxt.translate(-mWidth / 2, -mHeight / 2);

            if (i + 1 === mS) {
                //当前时
                cxt.fillStyle = mColorCur;
            } else {
                cxt.fillStyle = mColorCom;
            }
            cxt.font = mTextSizeCom + "px " + mFont;
            cxt.fillText(SECONDS[i], mWidth / 2 + mRadiusS, mCenterHeight);
            cxt.restore();
        }
        cxt.restore();
    }

    //刷新画布
    function drawCanvas() {
        updateBg();
        drawCenterInfo();
        drawHour();
        drawMinute();
        drawSecond();
    }

    //////////////////////////////////////////////////////////////////
    function curTime() {
        let date = new Date();
        let year = date.getFullYear();
        let month = date.getMonth() + 1;
        let day = date.getDate();
        let week = date.getDay();
        mH24 = date.getHours();
        mM = date.getMinutes();
        mS = date.getSeconds();
        mH = mH24 >= 12 ? mH24 - 12 : mH24;

        mTime = formatTime(mH24) + ':' + formatTime(mM) + ':' + formatTime(mS);
        mDate = year + '/' + formatTime(month) + '/' + formatTime(day);
        mWeek = parseWeek(week);
        calculateDegree();
    }

    function calculateDegree() {
        //逆时针旋转
        mDegreeH = -360 / 12 * (mH - 1);
        mDegreeM = -360 / 60 * (mM - 1);
        mDegreeS = -360 / 60 * (mS - 1);

        hd = mDegreeH;
        md = mDegreeM;
        sd = mDegreeS;
        av = 6;//每次动画总偏移量
    }

    function formatTime(fn) {
        return fn < 10 ? "0" + fn : fn;
    }

    function parseWeek(week) {
        var mWeek;
        switch (week) {
            case 1:
                mWeek = "星期一";
                break;
            case 2:
                mWeek = "星期二";
                break;
            case 3:
                mWeek = "星期三";
                break;
            case 4:
                mWeek = "星期四";
                break;
            case 5:
                mWeek = "星期五";
                break;
            case 6:
                mWeek = "星期六";
                break;
            case 7:
                mWeek = "星期日";
                break;
        }

        return mWeek;
    }
</script>
</body>
</html>